/*
 * MPLSHParamSelector.cpp
 *
 *  Created on: 2010-1-29
 *      Author: liuyi
 */

#include "MPLSHParamSelector.h"

#include <iostream>
#include <cassert>

#include "Toolkit.h"
#include "MPLSH.h"
#include "KNNTraversal.h"
#include "Sample.h"

using namespace std;

ParamGroup MPLSHParamSelector::select(SampleSpace &sampleSpace, vector<Sample *> sampleList,
		float samplingFactor, float acceptablePrecisionRate, unsigned int K,
		unsigned int MStart, unsigned int MStep, unsigned int MEnd,
		unsigned int LStart, unsigned int LStep, unsigned int LEnd,
		float WStart, float WStep, float WEnd) {

	if (MStep == 0 || LStep == 0 || WStep == 0) {
		assert(false);
	}

	int dim = sampleSpace.getDimension();

	unsigned int M = MStart;
	unsigned int L = LStart;
	float W = WStart;

	GuassRandom gr(1, 1);
	UniformRandom ur(0, W, false, false);

	float **a = NULL;
	float **b = NULL;
	vector<ParamGroup> paramList;

	vector<Sample *> subset;
	sampleSpace.sampling(sampleList, subset, samplingFactor);

	KNNTraversal knn(sampleSpace, false);
	knn.train(sampleList);

	knn.preClassify(K);
	knn.classifyArray(subset);

	ParamGroup bestParam;
	float score;

	while(true) {
		a = new float*[L];
		b = new float*[L];

		int aLenInTable = M*dim;
		for(unsigned int i = 0; i < L; ++i) {
			a[i] = new float[aLenInTable];
			b[i] = new float[M];

			gr.genRandomArray(a[i], aLenInTable);
			ur.genRandomArray(b[i], M);
		}

		float searchRatio = 0;
		float precisionRate = 0;
		evaluateParam(sampleSpace, sampleList, subset, K, M, L, W,
				a, b, &searchRatio, &precisionRate);

		cout << "search ratio = " << searchRatio << "\tprecision rate = "
				<< precisionRate << "\tM = " << M << "\tL = " << L <<
				"\tW = " << W << endl;

		score = ParamGroup::score(searchRatio, precisionRate);
		if(score > bestParam.score()) {
			bestParam = ParamGroup(M, L, W, searchRatio, precisionRate);
		}

		for(unsigned int i = 0; i < L; ++i) {
			delete[] a[i];
			delete[] b[i];
		}
		delete[] a;
		delete[] b;

		if(searchRatio >= 1 || precisionRate < acceptablePrecisionRate) {
			M = MEnd;
		}

		M += MStep;
		if(M > MEnd) {
			M = MStart;
			L += LStep;

			if(L > LEnd) {
				L = LStart;
				W += WStep;

				if(W > WEnd) {
					break;
				}
			}
		}
	}

	cout << "\nthe best parameter group is: \nscore = " << bestParam.score() << "\tsearch ratio = "
			<< bestParam.searchRatio << "\tprecision rate = " << bestParam.precisionRate
			<< "\tM = "	<< bestParam.M << "\tL = "	<< bestParam.L << "\tW = " << bestParam.W << endl;

	int subsetSize = subset.size();
	for(int i = 0; i < subsetSize; ++i) {
		sampleList.push_back(subset.at(i));
	}

	return bestParam;
}

void MPLSHParamSelector::evaluateParam(SampleSpace &sampleSpace, vector<Sample *> &sampleList,
		vector<Sample *> &subset, int K, int M, int L, float W, float **a, float **b,
		float *searchRatio, float *precisionRate) {

	MPLSH mplsh(sampleSpace, false);
	mplsh.preTrain(M, L, a, b, W);
	mplsh.train(sampleList);

	int subsetSize = subset.size();

	int rightCount = 0;
	int label;
	Sample *sample = NULL;

	mplsh.preClassify(K);
	for(int i = 0; i < subsetSize; ++i) {
		sample = subset.at(i);

		label = sample->getClass();
		*searchRatio += mplsh.classifyWithSearchRatio(*sample);

		if(label == sample->getClass()) {
			++rightCount;
		} else {
			sample->setClass(label);
		}
	}

	*searchRatio = *searchRatio / subsetSize;
	*precisionRate = rightCount * 1.0f / subsetSize;
}
